package com.xhai.admin.service;

import com.xhai.common.cache.RedisCache;
import com.xhai.common.constant.Constants;
import com.xhai.common.constant.RedisConstants;
import com.xhai.common.constant.RequestConstants;
import com.xhai.common.domain.login.LoginUser;
import com.xhai.common.utils.IdUtils;
import com.xhai.common.utils.classes.StringUtils;
import com.xhai.common.utils.encrypt.JwtUtils;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @author xhai
 * @description token验证处理
 * @date 2025/6/20
 */
@Component
public class TokenService {

    private static final Logger logger = LoggerFactory.getLogger(TokenService.class);
    private static final long MILLIS_SECOND = 1000;
    private static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;
    /**
     * token有效时间，分钟
     */
    @Value("${admin.jwt.expire-time}")
    private Integer TOKEN_EXPIRE_TIME;
    @Resource
    RedisCache redisCache;

    /**
     * 根据登录用户身份权限生成token令牌
     *
     * @param loginUser
     * @return
     */
    public String createToken(LoginUser loginUser) {
        // uuid
        String uuid = IdUtils.simpleUUID();
        loginUser.setToken(uuid);
        // 设置用户代理
        // 保存令牌和用户登陆权限信息到Redis并设置有效期
        saveLoginUserInRedisAndSetExpireTime(loginUser);

        Map<String, Object> claims = new HashMap<>();
        // 将uuid存放在令牌中中
        claims.put(Constants.LOGIN_USER_KEY, uuid);

        return JwtUtils.createToken(claims);
    }

    /**
     * 保存令牌和用户登陆权限信息到Redis并设置有效期
     *
     * @param loginUser
     */
    private void saveLoginUserInRedisAndSetExpireTime(LoginUser loginUser) {
        // 设置登陆时间
        loginUser.setLoginTime(System.currentTimeMillis());
        // 设置有效期
        loginUser.setExpireTime(loginUser.getLoginTime() + TOKEN_EXPIRE_TIME * MILLIS_MINUTE);
        // 根据token将用户登录信息保存到Redis，并设置有效期
        String redisLoginUserKey = RedisConstants.REDIS_KEY_PREFIX_LOGIN_USER + loginUser.getToken();
        redisCache.setCacheObject(redisLoginUserKey, loginUser, TOKEN_EXPIRE_TIME, TimeUnit.MINUTES);
    }

    /**
     * 根据请求中的token获取登录用户权限信息
     *
     * @param request
     * @return
     */
    public LoginUser getLoginUser(HttpServletRequest request) {
        // 获取请求头中的token
        String token = getToken(request);

        // 如果不为空，则获取redis中存储的用户信息，并设置认证权限信息
        if (!StringUtils.isEmpty(token)) {
            try {
                // 从token中获取令牌说明
                Claims claims = JwtUtils.parseJWT(token);
                // 获取uuid
                String uuid = (String) claims.get(Constants.LOGIN_USER_KEY);
                String redisLoginUserKey = RedisConstants.REDIS_KEY_PREFIX_LOGIN_USER + uuid;
                LoginUser loginUser = redisCache.getCacheObject(redisLoginUserKey);
                return loginUser;
            } catch (Exception e) {
                logger.error("根据token获取登录用户信息异常{}", e.getMessage());
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    /**
     * 获取请求token
     *
     * @param request
     * @return
     */
    public String getToken(HttpServletRequest request) {
        String token = request.getHeader(RequestConstants.REQUEST_HEADER_TOKEN);
        if (!StringUtils.isEmpty(token) && token.startsWith(RequestConstants.TOKEN_PREFIX)) {
            token = token.replace(RequestConstants.TOKEN_PREFIX, "");
        }
        return token;
    }

    /**
     * 刷新token有效期
     *
     * @param loginUser
     */
    public void refreshToken(LoginUser loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime <= TOKEN_EXPIRE_TIME * MILLIS_MINUTE) {
            saveLoginUserInRedisAndSetExpireTime(loginUser);
        }
    }
}
